/*
 * file:  expr.cpp (implementation of Expr).
 * Author: Jeremy Johnson 
 * Date: 2/5/07
 *
 * Description: 
 */

#include <iostream>
#include <string>
#include <map>
#include <list>
#include <cstdlib>
#include "programext.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HeapCell::HeapCell(int _cdr)
{
	mark = false;
	isPtr = false;
	isEmptyList = false;
	car = 0;
	cdr = _cdr;
}

void HeapCell::print()
{
	if(isPtr)
	{
		cout<<"ptr["<<car<<"]";
	}
	else
		cout<<car;
}

Heap::Heap(int size,map<string,Element> *_nameTable)
{
	nameTable = _nameTable;
	
	heapSize = size;
	available = 0;//initially available position is set to zero
	
	for(int i=0;i<heapSize-1;i++)
	{
		cells.push_back(HeapCell(i+1));
	}
	cells.push_back(HeapCell(-1));//denoting nil
}

int Heap::putValue(Element elm,int _cdr)
{
	if(isAvailable()==false)
	{
		cout << "Out of memory" << endl;
		exit(1);
	}	
	int memoryAddr = available;
	
	//keep temp name list for noname lists like: [1,2,3,[ ],5]
	if(_cdr == -1)
		tempNameTablePtr++;//insert a new noname list
	tempNameTable[tempNameTablePtr] = memoryAddr;
	
	updateAvailableCell();
	
	cells[memoryAddr].car = elm.value;
	cells[memoryAddr].cdr = _cdr;
	
	cells[memoryAddr].isPtr = elm.dataType==LIST?true:false;
	
	cells[memoryAddr].mark = true;
	cells[memoryAddr].isEmptyList = false;
	
	return memoryAddr;	
}

int Heap::putEmptyList()
{
	if(isAvailable()==false)
	{
		cout << "Out of memory" << endl;
		exit(1);
	}
	int memoryAddr = available;
	
	//keep temp name list for noname lists like: [1,2,3,[ ],5]
	tempNameTablePtr++;//insert a new noname list
	tempNameTable[tempNameTablePtr] = memoryAddr;
	
	updateAvailableCell();
	
	cells[memoryAddr].car = 0;//garbage
	cells[memoryAddr].cdr = -1;
	
	cells[memoryAddr].isPtr = false;
	
	cells[memoryAddr].mark = true;
	
	cells[memoryAddr].isEmptyList = true;
	
	return memoryAddr;	
}

void Heap::updateAvailableCell()
{
	available = cells[available].cdr;
}

bool Heap::isAvailable()
{
	if(available==-1)//if not available
		markAndSweep();//run mark and sweep algorithm to make garbage collection

	return available==-1?false:true;//if we open up some space, return true, else return false
}

void Heap::markAndSweep()
{
	map<string,Element>::iterator p;
	map<int,int>::iterator q;

	for (p = nameTable->begin();p != nameTable->end();p++)
	{
		if(p->second.dataType==LIST)
			mark(p->second.value);
	}
	for (q = tempNameTable.begin();q != tempNameTable.end();q++)
	{
		mark(q->second);
	}
	
	sweep();	
	unmark();	
}

void Heap::mark(int ptr)
{
	cells[ptr].mark = true;
	if(cells[ptr].isPtr==true)
		mark(cells[ptr].car);
	
	if(cells[ptr].cdr != -1)
		mark(cells[ptr].cdr);
}
void Heap::unmark()
{
	for(int i=0;i<heapSize;i++)
	{
		cells[i].mark = false;
	}
}
void Heap::sweep()
{
	for(int i=0;i<heapSize;i++)
	{
		if(cells[i].mark == false)
		{
			cells[i].car = 0;
			cells[i].cdr = available;
			
			cells[i].isPtr = false;			
			cells[i].isEmptyList = false;
			
			available = i;
		}
	}
}

void Heap::print()
{
	cout<<"Heap Car";
	cout<<"[";
	for(int i=0;i<heapSize-1;i++)
	{
		cells[i].print();
		cout<<",";
	}
	cells[heapSize-1].print();
	cout<<"]"<<endl;
	
	cout<<"Heap Cdr";
	cout<<"[";
	for(int i=0;i<heapSize-1;i++)
	{
		cout<<cells[i].cdr<<",";
	}
	cout<<cells[heapSize-1].cdr<<"]"<<endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Element List::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	heap->tempNameTablePtr = 0;
	heap->tempNameTable.clear();
	
	Element *elm = new Element(sequence->eval(NT,FT,FNT,heap,scoping,CT,OT).value,LIST);
	
	heap->tempNameTable.clear();	
	heap->unmark();
	return *elm;
}
//////////element functions
Element::Element(int _value, int _dataType)
{
	value = _value;	
	dataType = _dataType;
	
	identifier = "NULL";
}

Element::Element(string _identifier, int _dataType)
{
	identifier = _identifier;	
	dataType = _dataType;
	
	value = -11;
}

void Element::update(int _value, int _dataType)
{
	value = _value;	
	dataType = _dataType;
}

void Element::print(Heap *heap)
{//will be updated
	switch (dataType)
	{
		case LIST:
			printList(heap,value);
			break;
		case PROCEDURE:
			cout<<identifier;
			break;
		default:
			cout<<value;
	}
}

void Element::printList(Heap *heap, int position)
{
	int ptr=position;
	cout<<"[";
	while(1)
	{
		if(heap->cells[ptr].isEmptyList==true)
			break;
		else if(heap->cells[ptr].isPtr)
		{
			printList(heap,heap->cells[ptr].car);
		}
		else
		{
			cout<<heap->cells[ptr].car;
		}
		if(heap->cells[ptr].cdr==-1)
			break;		
		cout<<",";
		ptr = heap->cells[ptr].cdr;
	}
	cout<<"]";
}

Element& Element::operator=(const Element& elm)
{
	value = elm.value;
	identifier = elm.identifier;
	dataType = elm.dataType;
	
	return *this;
}

//////////////////////////
Element Cons::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	int memoryAddress;
	
	if(expr==NULL && lst==NULL)
	{
		memoryAddress = heap->putEmptyList();
	}
	else if(lst == NULL)
	{
		memoryAddress = heap->putValue(expr->eval(NT,FT,FNT,heap,scoping,CT,OT),-1);
	}
	else
	{
		memoryAddress = heap->putValue(expr->eval(NT,FT,FNT,heap,scoping,CT,OT),lst->eval(NT,FT,FNT,heap,scoping,CT,OT).value);
	}
	return Element(memoryAddress,LIST);
}

//List Functions
Element Concatenate::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm1,elm2;
	int memoryAddress;
	
	elm1 = l1->eval(NT,FT,FNT,heap,scoping,CT,OT);
	elm2 = l2->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	if(elm1.dataType!=LIST || elm2.dataType!=LIST)
	{
		cout<<"Syntax Error: concatenate operator requires second parameter to be a list"<<endl;
		exit(1);
	}
	
	vector<Element> temp;
	int ptr = elm1.value;
	bool type;
	while(1)
	{
		if(ptr == -1)
			break;
		
		if(heap->cells[ptr].isPtr==true)
			type = LIST;
		else
			type = VALUE;
		temp.push_back(Element(heap->cells[ptr].car,type));
		ptr = heap->cells[ptr].cdr;
	}
	memoryAddress =elm2.value;
	for(int i=temp.size()-1;i>=0;i--)
	{
		memoryAddress = heap->putValue(temp[i],memoryAddress);
	}
		
	return Element(memoryAddress,LIST);
}

Element Cdr::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm;

	elm = expr->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	if(elm.dataType!=LIST)
	{
		cout<<"Syntax Error: cdr function takes list as argument"<<endl;
		exit(1);
	}
	else
	{
		if(heap->cells[elm.value].cdr == -1)
			return Element(0,EMPTY_LIST);
		else
			return Element(heap->cells[elm.value].cdr,LIST);
	}
}

Element Car::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm;

	elm = expr->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	if(elm.dataType!=LIST)
	{
		cout<<"Syntax Error: car function takes list as argument"<<endl;
		exit(1);
	}
	else
	{
		if(heap->cells[elm.value].isEmptyList)
			return Element(0,EMPTY_LIST);
		if(heap->cells[elm.value].isPtr)
			return Element(heap->cells[elm.value].car,LIST);
		else
			return Element(heap->cells[elm.value].car,VALUE);
	}
}

Element Nullp::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm;
	elm = lst->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	bool returnVal;
	
	if(elm.dataType!=LIST)
	{
		cout<<"Syntax Error: nullp function takes list as argument"<<endl;
		exit(1);
	}
	else
	{
		returnVal = heap->cells[elm.value].isEmptyList;
	}
	
	return returnVal?Element(1,VALUE):Element(0,VALUE);
}

Element Intp::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm;
	elm = exp->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	if(elm.dataType==VALUE)
		return Element(1,VALUE);
	else
		return Element(0,VALUE);
}
Element Listp::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	Element elm;
	elm = exp->eval(NT,FT,FNT,heap,scoping,CT,OT);
	
	if(elm.dataType!=VALUE)
		return Element(1,VALUE);
	else
		return Element(0,VALUE);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Program::Program(StmtList *SL,int _memSize, int _scoping)
{
	NameTable_.clear();
	FunctionTable_.clear();
	ClassTable.clear();		//holds all class definitons
	SL_ = SL;
	memSize=_memSize;
	scoping = _scoping;
	buildHeap( &NameTable_);//supply heap size and nameTable as parameter
}

void Program::dump() 
{
  map<string,Element>::iterator p;
  map<string,Proc*>::iterator f;

  cout << "Dump of Symbol Table" << endl;
  cout << "Name Table" << endl;
  for (p = NameTable_.begin();p != NameTable_.end();p++)
  {
    cout << p->first << " -> " ; p->second.print(heap);cout << endl;
  }
  cout << "Function Table" << endl;
  for (f = FunctionTable_.begin();f != FunctionTable_.end();f++) 
    cout << f->first << endl;
}

void Program::eval() 
{
	SL_->eval(NameTable_, FunctionTable_, ProcNameTables, heap,scoping,ClassTable,ObjectTable);
}


void StmtList::insert(Stmt * S)
{
  SL_.push_front(S);
}

void StmtList::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  
{
  list<Stmt*>::iterator Sp;
  for (Sp = SL_.begin();Sp != SL_.end();Sp++)
	(*Sp)->eval(NT,FT,FNT,heap,scoping,CT,OT);
}

AssignStmt::AssignStmt(string name, Expr *E)
{
  name_ = name;
  E_ = E;
}

void AssignStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  const
{
	NT[name_] = E_->eval(NT,FT,FNT,heap,scoping,CT,OT);
}
////////////////
////////////oop class statement /////////////
ClassStmt::ClassStmt(string name, PlClass *P)
{
	name_ = name;
	P_ = P;
}




void ClassStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  const  
{
	CT[name_] = P_;
}
ClassStmt::~ClassStmt()
{
	delete P_;
}
//////////////////////
//////
DefineStmt::~DefineStmt()
{
  delete P_; 
}

DefineStmt::DefineStmt(string name, Proc *P)
{
  name_ = name;
  P_ = P;
}

void DefineStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  const
{
	FT[name_] = P_;
}

IfStmt::IfStmt(Expr *E, StmtList *S1, StmtList *S2)
{
  E_ = E;
  S1_ = S1;
  S2_ = S2;
}

IfStmt::~IfStmt() { delete E_; delete S1_; delete S2_; }

void IfStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  const
{
	if (E_->eval(NT,FT,FNT,heap,scoping,CT,OT).value > 0)
		S1_->eval(NT,FT,FNT,heap,scoping,CT,OT);
	else
		S2_->eval(NT,FT,FNT,heap,scoping,CT,OT);
}

WhileStmt::WhileStmt(Expr *E, StmtList *S)
{
  E_ = E;
  S_ = S;
}

WhileStmt::~WhileStmt() { delete E_; delete S_; }

void WhileStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT)  const
{
	while (E_->eval(NT,FT,FNT,heap,scoping,CT,OT).value > 0) 
		S_->eval(NT,FT,FNT,heap,scoping,CT,OT);
}

Number::Number(int value)
{
	value_ = value;
}

Element Number::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	return Element(value_,VALUE);
}

Ident::Ident(string name)
{
	name_ = name;
}

Element Ident::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	
	if(NT.find(name_) != NT.end())//can you find identifier at name table
		return NT[name_];
	if(FT.find(name_) != FT.end())//can you find identifier at function table
	{
		Element *elm = new Element(name_,PROCEDURE);
		return *elm;
	}
	if(CT.find(name_) != CT.end()){
		Element *elm = new Element(name_,PLCLASS);
		return *elm;

	}
	else
	{	
		cerr<<"undeclared variable: "<<name_<<endl;
		exit(1);
	}
}

Plus::Plus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Plus::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	return Element(op1_->eval(NT,FT,FNT,heap,scoping,CT,OT).value + op2_->eval(NT,FT,FNT,heap,scoping,CT,OT).value,VALUE);
}

Minus::Minus(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Minus::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	return Element(op1_->eval(NT,FT,FNT,heap,scoping,CT,OT).value - op2_->eval(NT,FT,FNT,heap,scoping,CT,OT).value,VALUE);
}

Times::Times(Expr* op1, Expr* op2)
{
	op1_ = op1;
	op2_ = op2;
}

Element Times::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	return Element(op1_->eval(NT,FT,FNT,heap,scoping,CT,OT).value * op2_->eval(NT,FT,FNT,heap,scoping,CT,OT).value,VALUE);
}

FunCall::FunCall(Expr *_exp, list<Expr*> *AL)
{
	AL_ = AL;
	exp = _exp;
	is_object = 0;
/*
	if(_exp->type() == T_IDENT || _exp->type() == T_FUNC)
		type_ = T_FUNC;
	else if(_exp->type() == T_CLASS)
		type_ = T_CLASS;
	else
		type_ = T_PROC;
*/
}

FunCall::FunCall(Expr *_exp, list<Expr*> *AL, Expr *objN)
{
	AL_ = AL;
	exp = _exp;
	objectName= objN;
	is_object = 1;
/*
	if(_exp->type() == T_IDENT || _exp->type() == T_FUNC)
		type_ = T_FUNC;
	else if(_exp->type() == T_CLASS)
		type_ = T_CLASS;
	else
		type_ = T_PROC;
*/
}



Element FunCall::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{

	
	

		
	if(is_object == 1 ){

		string objN= objectName->eval(NT,FT,FNT,heap,scoping,CT,OT).identifier;		

		

		if(OT.find(objN) != OT.end()){
			return OT[objN]->apply(NT, FT, heap, AL_, FNT,scoping,exp,CT,OT);

		}
		else{
			cerr<<"undefined object: "<< name_<<endl;
		}
	
	}
	string funcName = exp->eval(NT,FT,FNT,heap,scoping,CT,OT).identifier;

//	cout<<"Func Call: "<<funcName<<endl;
	if(NT.find(funcName) != NT.end())//can you find identifier at name table
		return FT[NT[funcName].identifier]->apply(NT, FT, heap, AL_, FNT,scoping,funcName,CT,OT);

	if(FT.find(funcName) != FT.end())//can you find identifier at function table
		return FT[funcName]->apply(NT, FT, heap, AL_, FNT,scoping,funcName,CT,OT);
	
	//if its a class
	if(CT.find(funcName) != CT.end()){
	
		return CT[funcName]->apply(NT, FT, heap, AL_, FNT,scoping,funcName,CT,OT);

	}

	cerr<<"undeclared function: "<< name_<<endl;
	exit(1);
}
///////////////////
//////////////////oop///////////////////////////////////////
PlClass::PlClass(list<string> *PL, StmtList *SL)
{
	SL_ = SL;
	PL_ = PL;
	NumParam_ = PL->size();
	hasParent = 0;
}

PlClass::PlClass(list<string> *PL, StmtList *SL,string parentName)
{
	SL_ = SL;
	PL_ = PL;
	NumParam_ = PL->size();
	pName = parentName;
	hasParent = 1;

}
Element PlClass::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	string className = "nonameClass";
	int num = FT.size();
	char numChar[10];
	sprintf(numChar,"%d",num);
	className.append(numChar);
	
	this->classN = className;
	CT[className] = this;
	
	Element *elm = new Element(className,PLCLASS);

	return *elm;
}

Element PlClass::apply(map<string,Element> &NT, map<string,Proc*> &FT, Heap *heap, list<Expr*> *EL, map<string,map<string,Element> > &FNT, int scoping, string funcName,map<string,PlClass*> &CT,map<string,PlObject*> &OT)
{



	map<string,Element> NNT;
	map<string,Proc*> NFT;
	NNT.clear();
	NFT.clear();

	// bind parameters in new name table

	list<string>::iterator p;
	list<Expr*>::iterator e;
	map<string,Element>::iterator n;
	map<string,Proc*>::iterator f;
	
	if (NumParam_ != EL->size()) {
		cout << "Param count does not match" << endl;
		exit(1);
	}
	

	
	for (p = PL_->begin(), e = EL->begin(); p != PL_->end(); p++, e++) 
			NNT[*p] = (*e)->eval(NT,FT,FNT,heap,scoping,CT,OT);


	PlObject *obj;
	if(hasParent == 0 ){
	
		obj = new PlObject( NNT,SL_);
	}
	else{
		obj = new PlObject( NNT,SL_,pName);
	}
	
	
	return obj->eval(NT,FT,FNT,heap,scoping,CT,OT);

}

PlObject::PlObject( map<string,Element> NT,  StmtList *SL)
{
	nameTable = NT;
	stmtL = SL;
	ProcNameTables.clear();
	FunctionTable_.clear();
	hasParent = 0;
}

PlObject::PlObject( map<string,Element> NT,  StmtList *SL,string pName_)
{
	nameTable = NT;
	stmtL = SL;
	ProcNameTables.clear();
	FunctionTable_.clear();
	hasParent = 1;
	pName = pName_;
}

Element PlObject::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	
	if(hasParent == 1){
		CT[pName]->SL_->eval(nameTable, FunctionTable_, ProcNameTables, heap,scoping,CT,OT);
	}
	stmtL->eval(nameTable, FunctionTable_, ProcNameTables, heap,scoping,CT,OT);

	
	string objName = "nonameObj";
	int num = FT.size();
	char numChar[10];
	sprintf(numChar,"%d",num);
	objName.append(numChar);


	OT[objName] = this;
	
	Element *elm = new Element(objName,OBJECT);

	return *elm;
}


Element PlObject::apply(map<string,Element> &NT, map<string,Proc*> &FT, Heap *heap, list<Expr*> *EL, map<string,map<string,Element> > &FNT, int scoping,Expr *exp,map<string,PlClass*> &CT,map<string,PlObject*> &OT)
{
	string funcName = exp->eval(nameTable,FunctionTable_,ProcNameTables,heap,scoping,CT,OT).identifier;
	
	

	cout<<"Func Call: "<<funcName<<endl;
	if(nameTable.find(funcName) != nameTable.end())//can you find identifier at name table
		return FunctionTable_[nameTable[funcName].identifier]->apply(nameTable, FunctionTable_, heap, EL, ProcNameTables,scoping,funcName,CT,OT);

	if(FunctionTable_.find(funcName) != FunctionTable_.end())//can you find identifier at function table
		return FunctionTable_[funcName]->apply(nameTable, FunctionTable_, heap, EL, ProcNameTables,scoping,funcName,CT,OT);
	
	

	cerr<<"undeclared function: "<< funcName <<endl;
	exit(1);

}


ObjFuncallStmt::ObjFuncallStmt( Expr *E_)
{
	E=E_;
}

ObjFuncallStmt::~ObjFuncallStmt()
{

}
void ObjFuncallStmt::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) const
{
	E->eval(NT,FT,FNT,heap,scoping,CT,OT);
}


/////////////////////////////////////////////
///////////////////

Proc::Proc(list<string> *PL, StmtList *SL)
{
	SL_ = SL;
	PL_ = PL;
	NumParam_ = PL->size();
	
}

Element Proc::apply(map<string,Element> &NT, map<string,Proc*> &FT, Heap *heap, list<Expr*> *EL,map<string,map<string,Element> > &FNT, int scoping, string funcName,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	map<string,Element> NNT;
	map<string,Proc*> NFT;
	NNT.clear();
	NFT.clear();

	// bind parameters in new name table

	list<string>::iterator p;
	list<Expr*>::iterator e;
	map<string,Element>::iterator n;
	map<string,Proc*>::iterator f;
	
	if (NumParam_ != EL->size()) {
		cout << "Param count does not match" << endl;
		exit(1);
	}
	//for static scoping we need to introduce global variables to the function scope, 
	for(n = NT.begin(); n!=NT.end(); n++)
			NNT[n->first] = n->second;
	
	if (FNT.find(funcName) != FNT.end())
		for(n = FNT[funcName].begin(); n != FNT[funcName].end(); n++)
			NNT[n->first] = n->second;
	
	//if we have dynamic scoping, overwrite what we have written above
	if(scoping == DYNAMIC )
		for(n = NT.begin(); n!=NT.end(); n++)
			NNT[n->first] = n->second;
	
	for (p = PL_->begin(), e = EL->begin(); p != PL_->end(); p++, e++) 
			NNT[*p] = (*e)->eval(NT,FT,FNT,heap,scoping,CT,OT);

/*
	cout<<endl<<"nameTable: "<<endl;
	for (n =  NNT.begin();n !=  NNT.end();n++)
		cout << n->first << " -> " << n->second.identifier<< "--"<<n->second.value << endl;
	cout << "Function Table" << endl;
	for (f = FT.begin();f != FT.end();f++) 
		cout << f->first << endl;
*/	

	// evaluate function body using new name table and old function table
	SL_->eval(NNT,FT,FNT,heap,scoping,CT,OT);//if we want functions to have local scope, run that part with NFT instead of FT

	if ( NNT.find("return") != NNT.end() )
		return NNT["return"];
	else {
		cout << "Error:  no return value" << endl;
		exit(1);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Element Proc::eval(map<string,Element> &NT, map<string,Proc*> &FT, map<string,map<string,Element> > &FNT, Heap *heap, int scoping,map<string,PlClass*> &CT,map<string,PlObject*> &OT) 
{
	string funcName = "nonameFunction";
	int num = FT.size();
	char numChar[10];
	sprintf(numChar,"%d",num);
	funcName.append(numChar);
	/*
		map<string,Element>::iterator n;
	cout<<"eval Proc:"<<funcName<<endl;
	cout<<endl<<"nameTable: "<<endl;
	for (n =  NT.begin();n !=  NT.end();n++)
		cout << n->first << " -> " << n->second.identifier<< "--"<<n->second.value << endl;
	cout<<"eval Proc name table end"<<endl;
	*/
	//SL_->eval(NT,FT,FNT,heap,scoping,CT,OT);
	//cout<<"evalProc eval ended, I will insert function now"<<endl;
	
	FNT[funcName] = NT;
	/*
	map<string,Element>::iterator n;
	cout<<"eval Proc:"<<funcName<<endl;
	cout<<endl<<"nameTable: "<<endl;
	for (n =  NT.begin();n !=  NT.end();n++)
		cout << n->first << " -> " << n->second.identifier<< "--"<<n->second.value << endl;
		
	cout<<endl<<"FNT nameTable: "<<endl;
	for (n =  FNT[funcName].begin();n !=  FNT[funcName].end();n++)
		cout << n->first << " -> " << n->second.identifier<< "--"<<n->second.value << endl;
	cout<<"eval Proc name table end"<<endl;
	*/
	
	FT[funcName] = this;
	
	Element *elm = new Element(funcName,PROCEDURE);

	return *elm;
}
